/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ua.yandex.collections;

/**
 *
 * @author Mariya
 */
public class MyLinkedList implements MyList {

    private static class Node {

        private Object element;
        private Node next;
        private Node prev;

        Node(Object e, Node prev, Node next) {
            element = e;
            this.next = next;
            this.prev = prev;
        }
    }

    private Node first;
    private Node last;
    private int size;

    MyLinkedList() {
        first = null;
        last = null;
        size = 0;
    }

    MyLinkedList(Object[] array) {
        if (array.length == 0) {
            first = null;
            last = null;
            size = 0;
        } else {
            first = new Node(array[0], null, null);
            size = array.length;
            Node iterator = first;
            for (int i = 1; i < array.length; i++) {
                iterator.next = new Node(array[i], iterator, null);
                iterator = iterator.next;
            }
            last = iterator;
        }
    }

    public void addFirst(Object e) {
        if (size == 0) {
            first = new Node(e, null, null);
        } else {
            Node newFirst = new Node(e, null, first);
            first.prev = newFirst;
            first = newFirst;
        }
        size++;
    }

    public void addLast(Object e) {
        if (size == 0) {
            first = new Node(e, null, null);
        } else {
            Node newLast = new Node(e, last, null);
            last.next = newLast;
            last = newLast;
        }
        size++;
    }

    @Override
    public void add(Object e) {
        addLast(e);
    }

    private void checkForExceptions(int index, boolean chekingEqual) {
        if (index > size || index < 0) {
            throw new IndexOutOfBoundsException();
        }
        if (chekingEqual && index == size) {
            throw new IndexOutOfBoundsException();
        }
    }

    private Node getNode(int index) {
        Node element = first;
        for (int i = 0; i < index; i++) {
            element = element.next;
        }
        return element;
    }

    @Override
    public void add(int index, Object e) {
        checkForExceptions(index, false);
        if (index == 0) {
            addFirst(e);
        } else {
            if (index < size) {
                Node elem = getNode(index - 1);
                Node newNode = new Node(e, elem, elem.next);
                size++;
                elem.next = newNode;
                elem.next.prev = newNode;
            } else {
                addLast(e);
            }
        }
    }

    @Override
    public void addAll(Object[] c) {
        for (int i = 0; i < c.length; i++) {
            addLast(c[i]);
        }
    }

    @Override
    public void addAll(int index, Object[] c) {
        checkForExceptions(index, false);
        if (index == 0) {
            for (int i = c.length - 1; i >= 0; i--) {
                addFirst(c[i]);
            }
        } else {
            if (index < size) {
                Node elem = getNode(index - 1);
                for (int i = 0; i < c.length; i++) {
                    Node newNode = new Node(c[i], elem, elem.next);
                    size++;
                    elem.next = newNode;
                    elem = elem.next;
                }

                elem.next.prev = elem;
            } else {
                for (Object d : c) {
                    addLast(d);
                }
            }
        }
    }

    public Object getFirst() {
        if (size == 0) {
            return null;
        } else {
            return first.element;
        }
    }

    public Object getLast() {
        if (size == 0) {
            return null;
        } else {
            return last.element;
        }
    }

    @Override
    public Object get(int index) {
        checkForExceptions(index, true);
        Node elem = getNode(index);
        return elem.element;
    }

    public Object removeFirst() {
        if (size == 0) {
            return null;
        } else {
            Object removed = first.element;
            size--;
            if (size == 0) {
                first = null;
                last = null;
            } else {
                first = first.next;
                first.prev = null;
            }
            return removed;
        }
    }

    public Object removeLast() {
        if (size == 0) {
            return null;
        } else {
            Object removed = last.element;
            size--;
            if (size == 0) {
                first = null;
                last = null;
            } else {
                last = last.prev;
                last.next = null;
            }
            return removed;
        }
    }

    @Override
    public Object remove(int index) {
        checkForExceptions(index, true);
        if (index == 0) {
            return removeFirst();
        } else {
            if (index == size - 1) {
                return removeLast();
            } else {
                Node elem = getNode(index);
                size--;
                elem.prev.next = elem.next;
                elem.next.prev = elem.prev;
                return elem.element;
            }
        }
    }

    @Override
    public void set(int index, Object e) {
        checkForExceptions(index, true);
        Node elem = getNode(index);
        elem.element = e;
    }

    @Override
    public int indexOf(Object o) {
        Node elem = first;
        int counter = 0;
        boolean b = false;
        while (!b && counter < size && elem.element != null) {
            if (elem.element.equals(o)) {
                b = true;
            } else {
                elem = elem.next;
                counter++;
            }
        }
        if (o == null && counter < size) {
            return counter;
        }
        if (b) {
            return counter;
        }
        return -1;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public void clear() {
        size = 0;
        first = null;
        last = null;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public Object[] toArray() {
        Object[] result = new Object[size];
        Node iterator = first;
        for (int i = 0; i < size; i++) {
            result[i] = iterator.element;
            iterator = iterator.next;
        }
        return result;
    }

    @Override
    public String toString() {
        if (size == 0) {
            return "";
        }
        Node iterator = first;
        StringBuilder buf = new StringBuilder();
        for (int i = 0; i < size - 1; i++) {
            buf.append(iterator.element);
            buf.append(",");
            iterator = iterator.next;
        }
        buf.append(iterator.element);
        return buf.toString();
    }

}
